/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package at.ofai.gate.riplugin2;

import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import pitt.search.semanticvectors.CompoundVectorBuilder;
import pitt.search.semanticvectors.LuceneUtils;
import pitt.search.semanticvectors.ObjectVector;
import pitt.search.semanticvectors.SearchResult;
import pitt.search.semanticvectors.VectorSearcher;
import pitt.search.semanticvectors.VectorStore;
import pitt.search.semanticvectors.VectorStoreRAM;
import pitt.search.semanticvectors.VectorStoreReader;
import pitt.search.semanticvectors.ZeroVectorException;
import pitt.search.semanticvectors.vectors.Vector;
import pitt.search.semanticvectors.vectors.VectorType;

/**
 * Class that initializes from a termvector store and a lucene index
 * and represents a SemanticVectors store.
 *
 * @author Johann Petrak
 */

// TODO: add support for using the docvectors!

public class SVIndex {

  VectorStore termVectorStore = null;
  VectorStore documentVectorStore = null;
  LuceneUtils luceneUtils = null;
  //VectorStoreRAM ramReader = new VectorStoreRAM(VectorType.REAL);
  boolean toLowerCase = true;

  /**
   * Constructor. The termVectorFileName has to be non-null and the file
   * name given must specify an existing term vector file. The other two
   * file names can be null to indicate that this data is not available.
   * 
   * @param termVectorFileName
   * @param documentVectorFileName
   * @param luceneIndexFileName
   * @throws IOException
   */

  public SVIndex(
    String termVectorFileName,
    String documentVectorFileName,
    String luceneIndexFileName)
          throws IOException {

    //ramReader.InitFromFile(termVectorFileName);
    //termVectorStore = ramReader;

    termVectorStore = VectorStoreReader.openVectorStore(termVectorFileName);

    if(documentVectorFileName != null) {
      documentVectorStore = VectorStoreReader.openVectorStore(documentVectorFileName);
    }
    // if the lucene index file name is blank or null, we do not use
    // the lucene index. In that case, no term weights will be used.
    if(luceneIndexFileName != null && !luceneIndexFileName.equals("")) {
      luceneUtils = new LuceneUtils(luceneIndexFileName);
    }
  }

  /**
   * This returns the maxresults most similar terms for a given term.
   * The list returns MatchinTerm objects that contain the term and the
   * matching score. The method can return less than maxresults results or,
   * in case of an error, no results at all (but will never return null).
   * 
   * @param stringA
   * @param maxresults
   * @return
   */
  public List<MatchingTerm> getSimilarTerms4Term(String term, int maxresults) {
    String[] args = new String[1];
    args[0] = term;
    return getSimilarTerms4Terms(args,maxresults);
  }
  public List<MatchingTerm> getSimilarTerms4Terms(String[] terms, int maxresults) {
    VectorSearcher vecSearcher = null;
    List<MatchingTerm> ret = new LinkedList<MatchingTerm>();
    try {
      vecSearcher =
        new VectorSearcher.VectorSearcherCosine(termVectorStore,termVectorStore,luceneUtils,terms);
    } catch (ZeroVectorException ex) {
      // TODO: log/show error message
      return ret;  // return an empty list
    }
    List<SearchResult> results = vecSearcher.getNearestNeighbors(maxresults);
    for(SearchResult r : results) {
      MatchingTerm m = new MatchingTerm(
        (float)r.getScore(),r.getObjectVector().getObject().toString());
      ret.add(m);
    }
    return ret;
  }


  public List<MatchingTerm> getSimilarDocids4Term(String term, int maxresults) {
    String[] args = new String[1];
    args[0] = term;
    return getSimilarDocids4Terms(args,maxresults);
  }

  public List<MatchingTerm> getSimilarDocids4Terms(String[] terms, int maxresults) {
    VectorSearcher vecSearcher = null;
    List<MatchingTerm> ret = new LinkedList<MatchingTerm>();
    try {
      vecSearcher =
        new VectorSearcher.VectorSearcherCosine(termVectorStore,documentVectorStore,luceneUtils,terms);
    } catch (ZeroVectorException ex) {
      // TODO: log/show error message
      return ret;  // return an empty list
    }
    List<SearchResult> results = vecSearcher.getNearestNeighbors(maxresults);
    for(SearchResult r : results) {
      MatchingTerm m = new MatchingTerm(
        (float)r.getScore(),r.getObjectVector().getObject().toString());
      ret.add(m);
    }
    return ret;

  }

  public List<String> getAllTerms() {
    List<String> results = new LinkedList<String>();
    Enumeration<ObjectVector> allVectors = termVectorStore.getAllVectors();
    while(allVectors.hasMoreElements()) {
      ObjectVector vec = allVectors.nextElement();
      results.add(vec.getObject().toString());
    }
    return results;
  }

  // default constructor not allowed
  private SVIndex() { 
  }

  public class MatchingTerm {
    public MatchingTerm(float score, String term) {
      this.score = score;
      this.term = term;
    }
    private float score;
    public float getScore() {
      return score;
    }
    private String term;
    public String getTerm() {
      return term;
    }
  }

  public float getTermTermSimilarity(String stringA, String stringB) {
    String[] termsA = new String[1];
    String[] termsB = new String[1];
    termsA[0] = stringA;
    termsB[0] = stringB;
    return getTermsTermsSimilarity(termsA, termsB);
  }

  public float getTermsTermsSimilarity(String[] termsA, String[] termsB) {
    //System.err.println("Comparing: "+v2s(termsA)+" and "+v2s(termsB));
    Vector v1 =
      CompoundVectorBuilder.getQueryVector(
        termVectorStore,luceneUtils,termsA);
    Vector v2 =
      CompoundVectorBuilder.getQueryVector(
        termVectorStore,luceneUtils,termsB);

    if(v1.isZeroVector()) {
      if(v2.isZeroVector()) {
        return -3;  // both are not found
      } else {
        return -1;  // v1 not found, v2 found
      }
    } else if(v2.isZeroVector()) {
      return -2;  // v1 found, v2 not found
    }

    double simScore = v1.measureOverlap(v2);
    return (float)simScore;
  }

  private <T> String v2s(T[] v) {
    String ret = "";
    for(T val : v) {
      ret = ret + val + " ";
    }
    return ret;
  }

  public void close() {
    // ANYTHING?
  }

  

  public static void main(String[] args) throws IOException, ZeroVectorException {
    // TODO: implement a "command" for each of the methods we implement
    if(args.length < 2) {
      System.err.println("Need at least 3 arguments: indexdir idxtyp command ...");
      System.err.println("where idxtyp is 'basic' or 'pos' and");
      System.err.println("where command and its args are one of the following:");
      System.err.println("  terms4term queryterm [maxresults]");
      System.err.println("  terms4terms maxresults term1 term2 [term3 ...]");
      System.err.println("  allterms");
      System.err.println("  docs4term queryterm [maxresults]");
      System.err.println("  term2term term1 term2");
      System.err.println("  terms2terms \"termset1\" \"termset2\"");
      System.exit(1);
    }
    String indexDirName = args[0];
    File indexDir = new File(indexDirName);

    if(!indexDir.exists()) {
      System.err.println("Index directory does not exist: "+indexDir.getCanonicalPath());
    }

    String idxtyp = args[1];
    if(!idxtyp.equals("basic") &&
       !idxtyp.equals("pos")) {
      System.err.println("Index type must be basic or pos");
      System.exit(1);
    }

    String command = args[2];
    if(
      !command.equals("terms4term") &&
      !command.equals("terms4terms") &&
      !command.equals("term2term") &&
      !command.equals("terms2terms") &&
      !command.equals("docs4term") &&
      !command.equals("allterms")) {
      System.err.println("Invalid command: "+command);
      System.exit(1);
    }

    File luceneIndexDir = new File(indexDir, "index");
    String luceneIndexDirName = null;
    if(luceneIndexDir.exists()) {
      luceneIndexDirName = luceneIndexDir.getCanonicalPath();
    }
    String termvecname = "termvectors.bin";
    if(idxtyp.equals("pos")) {
      termvecname = "postermvectors.bin";
    }
    File termVectorFile = new File(indexDir, termvecname);
    String termVectorFileName = null;
    if(termVectorFile.exists()) {
      termVectorFileName = termVectorFile.getCanonicalPath();
    } else {
      System.err.println("The term vector file does not exist: "+termVectorFile.getAbsolutePath());
    }
    String docvecname = "docvectors.bin";
    if(idxtyp.equals("pos")) {
      docvecname = "posdocvectors.bin";
    }
    File documentVectorFile = new File(indexDir, docvecname);
    String documentVectorFileName = null;
    if(documentVectorFile.exists()) {
      documentVectorFileName = documentVectorFile.getCanonicalPath();
    }
    SVIndex svIndex = new SVIndex(
      termVectorFileName,documentVectorFileName,luceneIndexDirName
    );
    if(command.equals("terms4term")) {
      if(args.length < 4) {
        System.err.println("Need 4 or 5 terms: term and optional max results ");
        System.exit(1);
      }
      String term = args[3];
      Integer maxresults = 10;
      if(args.length == 5) {
        maxresults = Integer.parseInt(args[4]);
      }
      for (MatchingTerm mt : svIndex.getSimilarTerms4Term(term, maxresults)) {
        System.out.println(mt.score+":"+mt.term);
      }
    } else if(command.equals("term2term") || command.equals("terms2terms")) {
      if(args.length != 5) {
        System.err.println("Need exactly 5 arguments");
        System.exit(1);
      }
      String arg1 = args[3];
      String arg2 = args[4];
      float sim = 0;
      if(command.equals("term2term")) {
        sim = svIndex.getTermTermSimilarity(arg1,arg2);
      } else {
        //sim = svIndex.getTermsTermsSimilarity(args2,args2);
      }
    } else if(command.equals("docs4term")) {
      if(args.length < 4) {
        System.err.println("Need 4 or 5 terms: term and optional max results ");
      }
      String term = args[3];
      Integer maxresults = 10;
      if(args.length >= 5) {
        Integer.parseInt(args[4]);
      }
      for (MatchingTerm mt : svIndex.getSimilarDocids4Term(term, maxresults)) {
        System.out.println(mt.score+":"+mt.term);
      }
      
    } else if(command.equals("allterms")) {
      for (String t : svIndex.getAllTerms()) {
        System.out.println(t);
      }
    } else if (command.equals("terms4terms")) {
      if(args.length < 6) {
        System.err.println("Need 6 or more terms: max results and 2 or more terms");
      }
      int maxresults = Integer.parseInt(args[3]);
      List<String> termList = new LinkedList<String>();
      for (int i=4; i<args.length; i++) {
        termList.add(args[i]);
      }
      String[] terms = termList.toArray(new String[0]);
      for (MatchingTerm mt : svIndex.getSimilarTerms4Terms(terms, maxresults)) {
        System.out.println(mt.score+":"+mt.term);
      }

    }
  }


}
